package me.chenqiang.cinc.codes;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;

import me.chenqiang.cinc.CincCodingException;

public class ISO3166Utils {
	
	public static class Record {
		private int index;
		private int numeric;
		private String alpha2;
		private String alpha3;
		private Record(int index, int numeric, String alpha2, String alpha3) {
			this.index = index;
			this.numeric = numeric;
			this.alpha2 = alpha2;
			this.alpha3 = alpha3;
		}
		
		public int getIndex() {
			return index;
		}

		public int getNumeric() {
			return numeric;
		}
		public String getAlpha2() {
			return alpha2;
		}
		public String getAlpha3() {
			return alpha3;
		}		
	}
	
	/**
	 * 对ISO 3166-1规定的国籍代码表进行转换的类。
	 * 数据来自于维基百科，以json格式保存。
	 */
	private static final Logger LOGGER = LoggerFactory.getLogger(ISO3166Utils.class);
	public static final List<Record> RECORDS;
	private static final Map<Integer, Record> INDEX_MAP;
	private static final Map<Integer, Record> NUMERIC_MAP;
	private static final Map<String, Record> ALPHA2_MAP;
	private static final Map<String, Record> ALPHA3_MAP;
	
	static {		
		HashMap<Integer, Record> numeric = new HashMap<>();
		HashMap<Integer, Record> index = new HashMap<>();
		HashMap<String, Record> alpha2 = new HashMap<>();		
		HashMap<String, Record> alpha3 = new HashMap<>();		
		List<Record> records = new ArrayList<>();
		
		try(InputStream jsonFile = ProvinceUtils.class.getResourceAsStream("code_iso_3166.json")) {
			ObjectMapper mapper = new ObjectMapper();
			ArrayNode list = (ArrayNode)mapper.readTree(new InputStreamReader(jsonFile, StandardCharsets.UTF_8));
			for(JsonNode country: list) {
				Record rec = new Record(country.get("index").asInt(), 
						country.get("numeric").asInt(),
						country.get("alpha-2").asText(), 
						country.get("alpha-3").asText());
				index.put(rec.getIndex(), rec);
				numeric.put(rec.getNumeric(), rec);
				alpha2.put(rec.getAlpha2(), rec);
				alpha3.put(rec.getAlpha3(), rec);
				records.add(rec);
			}
			
			LOGGER.debug("code_iso_3166.json文件加载完成{}项", list.size());
			
		} catch (IOException e) {
			LOGGER.error("code_iso_3166.json文件加载或解析失败", e);			
		} finally {
			RECORDS = Collections.unmodifiableList(records);
			INDEX_MAP = Collections.unmodifiableMap(index);
			NUMERIC_MAP = Collections.unmodifiableMap(numeric);
			ALPHA2_MAP = Collections.unmodifiableMap(alpha2);
			ALPHA3_MAP = Collections.unmodifiableMap(alpha3);
		}		
	}
	
	public static Record getCountryByAlpha3(String alpha3) throws CincCodingException {
		Record rec = ALPHA3_MAP.get(alpha3);
		if(rec == null) {
			throw new CincCodingException(String.format("三字母代码不存在：%s", alpha3));
		}
		return rec;
	}
	
	public static Record getCountryByAlpha2(String alpha2) throws CincCodingException {
		Record rec = ALPHA2_MAP.get(alpha2);
		if(rec == null) {
			throw new CincCodingException(String.format("两字母代码不存在：%s", alpha2));
		}
		return rec;
	}
	
	public static Record getCountryByNumeric(int numeric) throws CincCodingException {
		Record rec = NUMERIC_MAP.get(numeric);
		if(rec == null) {
			throw new CincCodingException(String.format("数字代码不存在：%s", numeric));
		}
		return rec;
	}
		
	public static Record getCountryByIndex(int index)  throws CincCodingException {
		Record rec = INDEX_MAP.get(index);
		if(rec == null) {
			throw new CincCodingException(String.format("国别序号不存在：%d", index));
		}
		return rec;
	}
	
	private static final Pattern NUMERIC_PATTERN = Pattern.compile("^\\d{1,3}$");
	private static final Pattern ALPHA2_PATTERN = Pattern.compile("^[A-Z]{2}$");
	private static final Pattern ALPHA3_PATTERN = Pattern.compile("^[A-Z]{3}$");
	public static Record getCountry(String code) throws CincCodingException {
		if(NUMERIC_PATTERN.matcher(code).matches()) {
			try {
				return getCountryByNumeric(Integer.parseInt(code));
			}
			catch(NumberFormatException nfe) {
				throw new CincCodingException(nfe);
			}
		}
		if(ALPHA2_PATTERN.matcher(code).matches()) {
			return getCountryByAlpha2(code);
		}
		if(ALPHA3_PATTERN.matcher(code).matches()) {
			return getCountryByAlpha3(code);
		}
		throw new CincCodingException(String.format("无法识别的国别代码：%s", code));
		
	}
	private ISO3166Utils() {}
}
